package com.xneure.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.xneure.utils.bean.BeanProcessor;
import com.xneure.utils.bean.JdbcHandler;
import com.xneure.utils.bean.RequestInfo;

@Component
public final class SpringUtil implements BeanFactoryPostProcessor,ApplicationListener<ContextRefreshedEvent>{
	private static String[] keywords = {"android","phone","wechat","ipod","ipad","mqqbrowser"};
	private static ApplicationContext wac;
	private static ConfigurableListableBeanFactory beanFactory;

	public static Map<String,String> getResourceAsMap(String name){
		Map<String,String> map = null;
		try{
			Properties _p = new Properties();
			_p.load(getResource(name).getInputStream());
			map = new HashMap<String,String>(_p.size());
			for(Entry<Object,Object> e:_p.entrySet()){
				map.put(e.getKey().toString().replace("_",""),e.getValue().toString());
			}
		}catch(IOException e){
		}
		return map;
	}

	public static ApplicationContext getSpringContext(){
		if(null == wac){
			try{
				wac = ContextLoader.getCurrentWebApplicationContext();
			}catch(Exception e){
			}
		}
		return wac;
	}

	public static Resource getResource(String name){
		return getSpringContext().getResource(name);
	}

	/**
	 * 获取系统默认的数据源 名称 = dataSource
	 * 
	 * @return DataSource
	 */
	public static DataSource getDataSource(){
		return getBean(DataSource.class);
	}

	public static JdbcTemplate getJdbcTemplate(){
		return new JdbcHandler(getDataSource());
	}

	/**
	 * 获取web服务的根路径
	 * 
	 * @return String
	 */
	public static String getWebRootPath(){
		return getRequest().getServletContext().getRealPath(File.pathSeparator);
	}

	/**
	 * 动态注册一个 spring bean
	 */
	public static void registerBean(Class<?>... cls){
		Object[] objs = new Object[cls.length];
		for(int i = 0;i < cls.length;i++){
			objs[i] = ObjectUtil.newInstance(cls[i]);
		}
		registerBean(objs);
	}

	public static void registerBean(Object... bean){
		ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory)getSpringContext().getAutowireCapableBeanFactory();
		for(Object c:bean){
			String simpleName = c.getClass().getSimpleName();
			beanFactory.registerSingleton(simpleName,c);
			char[] cs = simpleName.toCharArray();
			cs[0] = Character.toLowerCase(cs[0]);
			beanFactory.registerAlias(simpleName,String.valueOf(cs));
		}
	}

	/**
	 * 动态注册一个 spring bean
	 * 
	 * @return
	 */
	public static String getBeanName(Object obj){
		Class<?> cs = obj instanceof Class ? (Class<?>)obj : obj.getClass();
		String[] names = getSpringContext().getBeanNamesForType(cs);
		if(null == names){
			return null;
		}else{
			return names[0];
		}
	}

	/**
	 * 通过注解类型获取对象
	 * 
	 * @param annotationType
	 * @return Map<String,Object>
	 */
	public static Map<String,Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType){
		return getSpringContext().getBeansWithAnnotation(annotationType);
	}

	/**
	 * 获取指定的数据源
	 * 
	 * @param dsName
	 * @return DataSource
	 */
	public static DataSource getDataSource(String dsName){
		return getBean(dsName);
	}

	/**
	 * 获取javaBean
	 * 
	 * @param name
	 * @return Obj
	 */
	@SuppressWarnings("unchecked")
	public static <Obj> Obj getBean(String name){
		if(null != getSpringContext()){
			return (Obj)getSpringContext().getBean(name);
		}else{
			if(null != beanFactory){
				return (Obj)beanFactory.getBean(name);
			}
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public static <Obj> Obj getBean(String name,Object... args){
		if(null != getSpringContext()){
			Obj bean = (Obj)getSpringContext().getBean(name,args);
			return bean;
		}else{
			if(null != beanFactory){
				return (Obj)beanFactory.getBean(name,args);
			}
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public static <Obj> Obj getBean(Class<Obj> cls){
		if(null != getSpringContext()){
			Map<String,?> beansOfType = getSpringContext().getBeansOfType(cls);
			if(null != beansOfType){
				Iterator<?> iterator = beansOfType.values().iterator();
				if(iterator.hasNext()){
					return (Obj)iterator.next();
				}
			}
			return (Obj)null;
		}else{
			if(null != beanFactory){
				Map<String,?> beansOfType = beanFactory.getBeansOfType(cls);
				if(null != beansOfType){
					Iterator<?> iterator = beansOfType.values().iterator();
					if(iterator.hasNext()){
						return (Obj)iterator.next();
					}
				}
			}
			return null;
		}
	}

	public static <Obj> Obj getBean(Class<Obj> cls,Object... args){
		if(null != getSpringContext()){
			return (Obj)getSpringContext().getBean(cls,args);
		}else{
			if(null != beanFactory){
				return (Obj)beanFactory.getBean(cls,args);
			}
			return null;
		}
	}

	public static <Obj> Map<String,Obj> getBeanByCls(Class<Obj> cls){
		if(null != getSpringContext()){
			return getSpringContext().getBeansOfType(cls);
		}else{
			if(null != beanFactory){
				return beanFactory.getBeansOfType(cls);
			}
			return null;
		}
	}

	public static ServletContext getContext(){
		return getRequest().getServletContext();
	}

	public static String[] getBeanName(Class<?> cls){
		if(null != getSpringContext()){
			return getSpringContext().getBeanNamesForType(cls);
		}else{
			if(null != beanFactory){
				return beanFactory.getBeanNamesForType(cls);
			}
			return null;
		}
	}

	public static HttpServletRequest getRequest(){
		return getServletAttributes().getRequest();
	}

	public static HttpServletResponse getResponse(){
		return getServletAttributes().getResponse();
	}

	public static HttpSession getSession(){
		return getServletAttributes().getRequest().getSession();
	}

	public static ServletRequestAttributes getServletAttributes(){
		return (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
	}

	public static void returnJson(Object obj) throws IOException{
		returnString(JsonUtil.toJson(obj));
	}

	public static void returnString(String str) throws IOException{
		if(null == str){
			str = "null";
		}
		returnByte(str.getBytes("utf-8"));
	}

	public static void returnByte(byte[] b) throws IOException{
		returnByte(b,getResponse());
	}

	public static void returnJson(Object obj,HttpServletResponse res) throws IOException{
		returnString(JsonUtil.toJson(obj),res);
	}

	public static void returnString(String str,HttpServletResponse res) throws IOException{
		if(null == str){
			str = "null";
		}
		returnByte(str.getBytes("utf-8"),res);
	}

	public static void returnByte(byte[] b,HttpServletResponse response) throws IOException{
		response.setContentType("text/html;charset=utf-8");
		response.setCharacterEncoding("UTF-8");
		ServletOutputStream outputStream = response.getOutputStream();
		outputStream.write(b);
		outputStream.flush();
	}

	public static <Obj> Obj urlParamToObject(Class<Obj> o,HttpServletRequest request){
		return paramToObject(o,new HashMap<String,Object>(request.getParameterMap()),request.getCharacterEncoding());
	}

	/**
	 * 解析http请求中的参数
	 * 
	 * @param req
	 * @return String
	 */
	public static <Obj> Obj parseRequestParam(HttpServletRequest req,Class<Obj> cls){
		if(req.getParameterMap().size() > 0){
			return urlParamToObject(cls,req);
		}else{
			return httpStreamParamToObject(cls,req);
		}
	}

	public static <Obj> Obj parseRequestParam(Class<Obj> cls){
		HttpServletRequest req = getRequest();
		if(req.getParameterMap().size() > 0){
			return urlParamToObject(cls,req);
		}else{
			return httpStreamParamToObject(cls,req);
		}
	}

	@SuppressWarnings("unchecked")
	public static Map<String,Object> parseRequestParam(){
		HttpServletRequest req = getRequest();
		if(req.getParameterMap().size() > 0){
			return urlParamToObject(Map.class,req);
		}else{
			return httpStreamParamToObject(Map.class,req);
		}
	}

	/**
	 * 获取流参数
	 * 
	 * @param o
	 * @param req
	 * @return Obj
	 */
	public static <Obj> Obj httpStreamParamToObject(Class<Obj> o,HttpServletRequest req){
		try{
			ServletInputStream is = req.getInputStream();
			InputStreamReader sr = new InputStreamReader(is,"utf-8");
			BufferedReader br = new BufferedReader(sr);
			String line = null;
			StringBuilder sb = new StringBuilder(1024);
			while((line = br.readLine()) != null){
				sb.append(line);
			}
			br.close();
			if(sb.length() < 2){
				return null;
			}
			return JsonUtil.toObject(sb.toString(),o);
		}catch(Exception e){
			return null;
		}
	}

	@SuppressWarnings({"unchecked"})
	public static <Obj> Obj paramToObject(Class<Obj> o,Map<String,Object> map,String encoding){
		Obj tempObj = null;
		if(null == map){
			return tempObj;
		}
		if(o.isAssignableFrom(Map.class)){
			Map<String,Object> _map = new LinkedHashMap<String,Object>(map.size());
			for(Map.Entry<String,Object> entry:map.entrySet()){
				if(entry.getKey().contains("[")){
					String[] tmp = entry.getKey().split("\\[");
					Map<String,Object> mapzTmp = (Map<String,Object>)_map.get(tmp[0]);
					if(null == map){
						mapzTmp = new HashMap<String,Object>();
						_map.put(tmp[0],mapzTmp);
					}
					_map.put(tmp[1].substring(0,tmp[1].length() - 1),arrayConvert(entry.getValue(),",",encoding));
				}else{
					if(entry.getValue().getClass().isArray()){
						Object[] os = (Object[])entry.getValue();
						if(os.length == 1){
							_map.put(entry.getKey(),decode(os[0].toString(),encoding));
							continue;
						}
					}
					_map.put(entry.getKey(),arrayConvert(entry.getValue(),",",encoding));
				}
			}
			tempObj = (Obj)_map;
		}else{
			tempObj = ObjectUtil.newInstance(o);
			BeanProcessor objMap = new BeanProcessor(tempObj);
			for(Map.Entry<String,Object> entry:map.entrySet()){
				objMap.setValue(entry.getKey(),arrayConvert(entry.getValue(),",",encoding));
			}
			objMap = null;
		}
		return tempObj;
	}

	public static Resource[] scanResources(String path){
		try{
			return getSpringContext().getResources(path);
		}catch(IOException e){
			return null;
		}
	}

	public static String decode(String str,String encoding){
		try{
			if(str.contains("%") || str.contains("+")){
				if(null == encoding || encoding.isEmpty()){
					encoding = "utf-8";
				}
				return URLDecoder.decode(str,encoding);
			}else{
				return str;
			}
		}catch(UnsupportedEncodingException e){
			return str;
		}
	}

	public static Object arrayConvert(Object obj,String mak,String encoding){
		if(ObjectUtil.isNotNull(obj)){
			if(obj.getClass().isArray() == false){
				if(obj.toString().indexOf(mak) > 0){
					String[] tmp = obj.toString().split(mak);
					for(int i = 0;i < tmp.length;i++){
						tmp[i] = decode(tmp[i],encoding);
					}
					obj = tmp;
				}
			}else{
				Object[] objects = (Object[])obj;
				if(objects.length < 2){
					obj = arrayConvert(decode(objects[0].toString(),encoding),mak,encoding);
				}
			}
		}
		return obj;
	}

	public static String getClientIP(){
		return getClientIP(getRequest());
	}

	public static RequestInfo getRequestInfo(){
		return new RequestInfo(getRequest());
	}

	public static String getClientIP(HttpServletRequest request){
		String[] cip = new String[]{request.getHeader("x-forwarded-for"),request.getHeader("Proxy-Client-IP"),request.getHeader("WL-Proxy-Client-IP"),
				request.getRemoteAddr()};
		String ip = "";
		for(String s:cip){
			if(null != s && "unknown".equalsIgnoreCase(ip) == false){
				ip = s;
				break;
			}
		}
		if(ip.length() < 5){
			ip = "127.0.0.1";
		}else if(ip.contains(",")){
			ip = ip.substring(0,ip.indexOf(","));
		}
		return ip;
	}

	/**
	 * 获取本机Ip 通过 获取系统所有的networkInterface网络接口 然后遍历 每个网络下的InterfaceAddress组。 获得符合
	 * <code>InetAddress instanceof Inet4Address</code> 条件的一个IpV4地址
	 */
	@SuppressWarnings("rawtypes")
	public static String localIp(){
		Enumeration allNetInterfaces;
		String serverIp = null;
		try{
			allNetInterfaces = NetworkInterface.getNetworkInterfaces();
			while(allNetInterfaces.hasMoreElements()){
				NetworkInterface netInterface = (NetworkInterface)allNetInterfaces.nextElement();
				List<InterfaceAddress> InterfaceAddress = netInterface.getInterfaceAddresses();
				for(InterfaceAddress add:InterfaceAddress){
					InetAddress Ip = add.getAddress();
					if(Ip != null && Ip instanceof Inet4Address){
						serverIp = Ip.getHostAddress();
					}
				}
			}
		}catch(SocketException e){
		}
		return serverIp;
	}

	public static boolean checkAgent(HttpServletRequest request){
		boolean flag = false;
		try{
			String agent = request.getHeader("user-agent").toLowerCase();
			for(String item:keywords){
				if(agent.contains(item)){
					flag = true;
					break;
				}
			}
		}catch(Exception e){
		}
		return flag;
	}

	public static String getServerUrl(){
		HttpServletRequest request = getRequest();
		return request.getScheme() + "://" + request.getServerName() + (request.getServerPort() == 80 ? "" : ":" + request.getServerPort())
				+ request.getContextPath();
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException{
		SpringUtil.setBeanFactory(beanFactory);
	}

	private static void setBeanFactory(ConfigurableListableBeanFactory beanFactory){
		SpringUtil.beanFactory = beanFactory;
	}

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event){
		if(null == event.getApplicationContext().getParent()){
			SpringUtil.setWac(event.getApplicationContext());
		}
	}

	private static void setWac(ApplicationContext applicationContext){
		SpringUtil.wac = applicationContext;
	}
}
